/*   tuner.c
 * 
 *   A simple command line guitar tuner.
 *   Uses OSS at the moment, probably ALSA later.
 *
 *   Copyright 2009, Krzysztof Chalupka
 *   Email:  janchatko@gmail.com
 *
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/soundcard.h>

#include <math.h>
#include <fftw3.h>

int fd_in;
int sample_rate = 8192;

static int open_device(char *name, int mode){
    int tmp, fd;

    if ((fd = open(name, mode, 0)) == -1){
        perror(name);
        exit(-1);
    }

    tmp = AFMT_S16_NE;
    if (ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) == -1){
        perror("SNODCTL_DSP_SETFMT");
        exit(-1);
    }

    tmp = 1;
    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) == -1){
        perror("SNDCTL_DSP_CHANNELS");
        exit(-1);
    }

    if (ioctl(fd, SNDCTL_DSP_SPEED, &sample_rate) == -1){
        perror("SNDCTL_DSP_SPEED");
        exit(-1);
    }

    return fd;
}

int find_peak_freq(double *in, fftw_complex *out, fftw_plan p){
    int l, i;
    short buf[sample_rate];
    if ((l = read(fd_in, buf, sizeof(buf))) == -1){
        perror("Audio read");
        exit(-1);
    }

    // Go to the freq domain using libfft3.
    for(i = 0; i < sample_rate; i++){
         in[i] = buf[i];
    }
    fftw_execute(p);

    // Find the frequency with most contribution.
    double max = 0;
    int strongest;
    double absval;
    for(i = 0; i < sample_rate; i++){
        absval = out[i][0] * out[i][0] + out[i][1] * out[i][1];
        if(absval > max){
           max = absval;
           strongest = i;
        } 
    }

    return strongest;
}

int main(int argc, char *argv[]){
    int i;
    
    sample_rate = 48000;
    if(argc > 1){
        fd_in = open_device(argv[1], O_RDONLY);
    }
    else{    
        fd_in = open_device("/dev/audio", O_RDONLY);
    }


    double *in = (double *) fftw_malloc(sample_rate * sizeof(double));
    fftw_complex *out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * sample_rate);
    fftw_plan p;
    p = fftw_plan_dft_r2c_1d(sample_rate, in, out, FFTW_ESTIMATE);

    int strongest;
    printf("**************************************************\
    \n*    Command Tuner 0.1                           *\
    \n*    Assumed standard tuning (EAdgbe).           *\
    \n*    by Krzysztof Chalupka                       *\
    **************************************************");
    while(1){
        strongest = find_peak_freq(in, out, p);

        if(strongest == 220){
            continue;
        }       
        else{
            printf("\n");
        }
        if(strongest > 329){
            printf("\te + %d", strongest - 329);
        }else if(strongest > 284){
            printf("\te - %d", 329 - strongest);
        }else if(strongest > 246.9){
            printf("\tb + %f", strongest - 246.9);
        }else if(strongest > 221){
            printf("\tb - %f", 246.9 - strongest);
        }else if(strongest > 196){
            printf("\tg + %d", strongest - 196);
        }else if(strongest > 171.4){
            printf("\tg - %d", 196 - strongest);
        }else if(strongest > 146.8){
            printf("\td + %f", strongest - 146.8);
        }else if(strongest > 128.4){
            printf("\td - %f", 146.8 - strongest);
        }else if(strongest > 110){
            printf("\tA + %d", strongest - 110);
        }else if(strongest > 96.2){
            printf("\tA - %d", 110 - strongest);
        }else if(strongest > 82.4){
            printf("\tE + %f", strongest - 82.4);
        }else{
            printf("\tE - %f", 82.4 - strongest);
        }
       
    }
    printf("Wow, that was close to 440Hz. Actually, you got %d. Thanks, bye!", strongest);
    exit(0);
}
